using System;
using System.Collections.Generic;
using System.Linq;
using Magiq.Behaviors;
using Magiq.Delete;
using Magiq.Insert;
using Magiq.Objects;
using Magiq.Query;
using Magiq.Support;
using Magiq.Update;

namespace Magiq
{
    public static class Magiq
    {
        private static readonly MagiqToObjects Default = new MagiqToObjects();
        private static readonly IList<IMagiqProvider> Providers = new List<IMagiqProvider>();
        
        private static readonly IMagiqBehavior behaviorChain = new NullBehavior();

        public static IMagiqBehavior BehaviorChain { get { return behaviorChain; } }
        
        static Magiq()
        {
            Register(Default);
        }

        public static IMagiqConfigurator NoDefault()
        {
            return new Configurator().NoDefault();
        }

        public static IMagiqConfigurator Register<T>() where T : IMagiqProvider, new()
        {
            return new Configurator().Register<T>();
        }

        public static IMagiqConfigurator Register(IMagiqProvider magiqToSomething)
        {
            return new Configurator().Register(magiqToSomething);
        }

        private static TStrategy GetStrategy<TStrategy>(Func<IMagiqProvider, TStrategy> getStrategy)
            where TStrategy : class
        {
            return Providers.Select(getStrategy).First(x => x != null);
        }

        public static IUpdateStrategy ForUpdate<T>(IUpdate<T> update) where T : class
        {
            return GetStrategy(x => x.GetUpdateStrategy(update));
        }

        public static IDeleteStrategy ForDelete<T>(IDelete<T> delete) where T : class
        {
            return GetStrategy(x => x.GetDeleteStrategy(delete));
        }

        public static IQueryStrategy<T> ForQuery<T>(IQuery<T> query) where T : class
        {
            return GetStrategy(x => x.GetQueryStrategy(query));
        }

        public static IInsertStrategy ForInsert<TSource, TDestination, TNew>(IInsert<TSource, TDestination, TNew> insert)
            where TDestination : class where TNew : class, TDestination, new()
        {
            return GetStrategy(x => x.GetInsertStrategy(insert));
        }

        public static IInsertDestination<TNew> ForInsertDestination<TSource, TDestination, TNew>(
            IInsert<TSource, TDestination, TNew> insertDestinationDefinition)
            where TDestination : class
            where TNew : class, TDestination, new()
        {
            return GetStrategy(x => x.GetInsertDestination(insertDestinationDefinition));
        }

        public static IMagiqConfigurator Clear()
        {
            Providers.Clear();
            return Register(Default);
        }

        #region Nested type: Configurator

        private class Configurator : IMagiqConfigurator
        {
            public IMagiqConfigurator Register<T>() where T : IMagiqProvider, new()
            {
                return Register(new T());
            }

            public IMagiqConfigurator Register(IMagiqProvider magiqToSomething)
            {
                Providers.Insert(0, magiqToSomething);
                return this;
            }

            public IMagiqConfigurator NoDefault()
            {
                if (Providers.Contains(Default))
                    Providers.Remove(Default);
                return this;
            }

            public IMagiqConfigurator Use<TBehavior>() where TBehavior : IMagiqBehavior, new()
            {
                return Use(new TBehavior());
            }

            public IMagiqConfigurator Use(IMagiqBehavior behavior)
            {
                behaviorChain.NextBehavior = behavior;
                return this;
            }
        }

        #endregion

        public static IMagiqConfigurator Use<TBehavior>() where TBehavior : IMagiqBehavior, new()
        {
            return new Configurator().Use<TBehavior>();
        }

        public static IMagiqConfigurator Use(IMagiqBehavior behavior)
        {
            return new Configurator().Use(behavior);
        }
    }
}